package org.sfx.rich

import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import scala.reflect.Method
import org.sfx.rich.Enrich._
import org.sfx.util.BooleanGetter
import org.sfx.util.BooleanSetter
import org.sfx.util.DoubleGetter
import org.sfx.util.DoubleSetter
import org.sfx.util.Interpreter
import org.sfx.util.IntGetter
import org.sfx.util.IntSetter
import org.sfx.util.ObjectGetter
import org.sfx.util.ObjectSetter
import org.sfx.util.StringGetter
import org.sfx.util.StringSetter
import org.sfx.util.TypeWrangler
import javafx.beans.value.WritableValue
import org.sfx.util.TypeWrangler.javaType
import org.sfx.util.Interpreter.manifestOf

case class RichObject(val item: Object) {
  def _class = item.getClass()
  def _get(name: String) = _class.cache.getters("name")(item)()
  def _properties = this._class.cache.properties.map { propertyMethod =>
    propertyMethod.invoke(item) match {
      case writableValue: WritableValue[Object] => {
        val returnType = propertyMethod.genericReturnType
        println("############# " + returnType + " <" + propertyMethod.name + "> " + RichObject.writableStringType)
        val accessor = if (javaType(returnType) <::< manifestOf[WritableValue[java.lang.Boolean]]) {
          val writableBooleanValue = writableValue.asInstanceOf[WritableValue[Boolean]]
          new BooleanSetter with ObjectSetter {
            override def name = propertyMethod.name
            override def subject = {
              writableBooleanValue
            }
            override def accessorType = returnType
            override def booleanValue = Option(writableBooleanValue.getValue()).getOrElse(false)
            override def booleanValue_=(value: Boolean): Unit = {
              writableBooleanValue.setValue(value)
            }
            override def objectValue = writableValue.getValue()
            override def objectValue_=(value: Object): Unit = writableValue.setValue(value)
          }
        } else if (javaType(returnType) <::< manifestOf[WritableValue[String]]) {
          new StringSetter with ObjectSetter {
            override def name = propertyMethod.name
            override def subject = {
              writableValue
            }
            override def accessorType = returnType
            override def stringValue = Option(writableValue.getValue()).getOrElse("null").toString()
            override def stringValue_=(value: String): Unit = {
              writableValue.setValue(value)
            }
            override def objectValue = writableValue.getValue()
            override def objectValue_=(value: Object): Unit = writableValue.setValue(value)
          }
        } else if (javaType(returnType) <::< manifestOf[WritableValue[java.lang.Integer]]) {
          new IntSetter with StringSetter with ObjectSetter {
            override def name = propertyMethod.name
            override def subject = {
              writableValue
            }
            override def accessorType = returnType
            override def intValue = writableValue.asInstanceOf[WritableValue[java.lang.Integer]].getValue()
            override def intValue_=(value: Int): Unit = writableValue.asInstanceOf[WritableValue[java.lang.Integer]].setValue(value)
            override def stringValue = Option(writableValue.getValue()).getOrElse("null").toString()
            override def stringValue_=(value: String): Unit = try {
              intValue = java.lang.Integer.parseInt(value)
            } catch { case e =>
            }
            override def objectValue = writableValue.getValue()
            override def objectValue_=(value: Object): Unit = writableValue.setValue(value)
          }
        } else if (javaType(returnType) <::< manifestOf[WritableValue[java.lang.Double]] ||
            javaType(returnType) <::< manifestOf[WritableValue[java.lang.Number]]) {
          new DoubleSetter with StringSetter with ObjectSetter {
            override def name = propertyMethod.name
            override def subject = {
              writableValue
            }
            override def accessorType = returnType
            override def doubleValue = writableValue.asInstanceOf[WritableValue[java.lang.Double]].getValue()
            override def doubleValue_=(value: Double): Unit = writableValue.asInstanceOf[WritableValue[java.lang.Double]].setValue(value)
            override def stringValue = Option(writableValue.getValue()).getOrElse("null").toString()
            override def stringValue_=(value: String): Unit = try {
              doubleValue = java.lang.Double.parseDouble(value)
            } catch { case e =>
            }
            override def objectValue = writableValue.getValue()
            override def objectValue_=(value: Object): Unit = writableValue.setValue(value)
          }
        } else {
          println("--> unknown setter")
          new StringGetter with ObjectGetter {
            override def name = propertyMethod.name
            override def subject = {
              println("---> ccc")
              writableValue
            }
            override def accessorType = returnType
            override def stringValue = Option(writableValue.getValue()).getOrElse("null").toString()
            override def objectValue = writableValue.getValue()
          }
        }
        accessor
      }
      case observableValue: ObservableValue[Object] => {
        val returnType = propertyMethod.genericReturnType
        val accessor = new StringGetter with ObjectGetter {
          override def name = propertyMethod.name
            override def subject = {
              println("---> ddd")
              observableValue
            }
          override def accessorType = returnType
          override def stringValue = Option(observableValue.getValue()).getOrElse("null").toString()
          override def objectValue = observableValue.getValue()
        }
        accessor
      }
    }
  }
  def _observableLists = this._class.cache.observableLists.map { method =>
    (method.name -> method.invoke(item).asInstanceOf[ObservableList[Nothing]])
  }.toMap
  override def toString(): String = {
    throw new RuntimeException("do not call")
  }
}

object RichObject {
  val writableBooleanType = new { def foo: WritableValue[java.lang.Boolean] = null }.getClass().method("foo").genericReturnType
  val writableByteType = new { def foo: WritableValue[java.lang.Byte] = null }.getClass().method("foo").genericReturnType
  val writableCharacterType = new { def foo: WritableValue[java.lang.Character] = null }.getClass().method("foo").genericReturnType
  val writableShortType = new { def foo: WritableValue[java.lang.Short] = null }.getClass().method("foo").genericReturnType
  val writableIntegerType = new { def foo: WritableValue[java.lang.Integer] = null }.getClass().method("foo").genericReturnType
  val writableLongType = new { def foo: WritableValue[java.lang.Long] = null }.getClass().method("foo").genericReturnType
  val writableFloatType = new { def foo: WritableValue[java.lang.Float] = null }.getClass().method("foo").genericReturnType
  val writableNumberType = new { def foo: WritableValue[java.lang.Number] = null }.getClass().method("foo").genericReturnType
  val writableDoubleType = new { def foo: WritableValue[java.lang.Double] = null }.getClass().method("foo").genericReturnType
  val writableStringType = new { def foo: WritableValue[java.lang.String] = null }.getClass().method("foo").genericReturnType
  val observableBooleanType = new { def foo: ObservableValue[java.lang.Boolean] = null }.getClass().method("foo").genericReturnType
  val observableByteType = new { def foo: ObservableValue[java.lang.Byte] = null }.getClass().method("foo").genericReturnType
  val observableCharacterType = new { def foo: ObservableValue[java.lang.Character] = null }.getClass().method("foo").genericReturnType
  val observableShortType = new { def foo: ObservableValue[java.lang.Short] = null }.getClass().method("foo").genericReturnType
  val observableIntegerType = new { def foo: ObservableValue[java.lang.Integer] = null }.getClass().method("foo").genericReturnType
  val observableLongType = new { def foo: ObservableValue[java.lang.Long] = null }.getClass().method("foo").genericReturnType
  val observableStringType = new { def foo: ObservableValue[java.lang.String] = null }.getClass().method("foo").genericReturnType
  
  def main(args: Array[String]): Unit = {
    println(observableIntegerType)
  }
}
